package com.jeeccm.modules.web.jwt.interceptors;

import com.jeeccm.modules.web.jwt.dto.ClientDTO;
import com.jeeccm.modules.web.jwt.dto.JwtConstants;
import com.jeeccm.modules.web.jwt.service.TokenService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * 类名：RestTokenInterceptor.java<br>
 * 描述：Restful请求， Token校验规则拦截器（JWT）<br>
 * @author ：XINJF<br>
 * 创建日期：2018年3月21日 下午5:03:49<br>
 * 版本：1.0<br>
 * 修改者：<br>
 * 修改日期：<br>
 */
@Component
public class RestAuthTokenInterceptor implements HandlerInterceptor {
	
	private static Logger logger = LoggerFactory.getLogger(RestAuthTokenInterceptor.class);

	@Autowired
	private TokenService tokenService;

	/**
	 * 包含匹配（请求链接包含该配置链接，就进行过滤处理）
	 */
	private List<String> excludeUrls;

	private List<String> excludeContainUrls;

	/**
	 * URL需求拦截某个路径下的URL,比如拦截/rest/registUser
	 * 									  /rest/login.action
	 * 用户根据不同的项目配置进行指定
	 */
	private String startRequestPaht = "/rest/";

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		final String requestPath = request.getRequestURI().substring(request.getContextPath().length());
		System.out.println("requestPath==" + requestPath);
		final int requestPathIndex = requestPath.indexOf(startRequestPaht);
		if(requestPathIndex == -1 || excludeUrls.contains(requestPath) || this.vagueContain(excludeContainUrls, requestPath)){
			return true;
		}

		//从header中得到token
		final String authHeader = request.getHeader(JwtConstants.AUTHORIZATION);
		if(StringUtils.isEmpty(authHeader)){
			throw new ServletException("Missing or invalid X-AUTH-TOKEN header.");
		}

		// 验证token
		Claims claims = null;
		try{
			claims = Jwts.parser().setSigningKey(JwtConstants.JWT_SECRET).parseClaimsJws(authHeader).getBody();
		}catch (final SignatureException e){
			throw new ServletException("Invalid token.");
		}

		Object username = claims.getId();
		if (StringUtils.isEmpty(username.toString())){
			throw new ServletException("Invalid X-AUTH-TOKEN Subject no exist username.");
		}

		final ClientDTO clientDTO = this.tokenService.getToken(authHeader,username.toString());
		final boolean checkToken = this.tokenService.checkToken(clientDTO);
		if(checkToken){
			//如果token验证成功，个求将对象传递给下一请
			request.setAttribute(JwtConstants.CURRENT_TOKEN_CLAIMS, claims);
			//如果token验证成功，将token对应的用户id存在request中，便于之后注入
			request.setAttribute(JwtConstants.CURRENT_USER_NAME, clientDTO.getUserName());
			return true;
		}else {
			// 如果验证token失败，则返回401错误
			response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
			return false;
		}
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

	}

	public List<String> getExcludeUrls() {
		return excludeUrls;
	}

	public void setExcludeUrls(List<String> excludeUrls) {
		this.excludeUrls = excludeUrls;
	}

	public List<String> getExcludeContainUrls() {
		return excludeContainUrls;
	}

	public void setExcludeContainUrls(List<String> excludeContainUrls) {
		this.excludeContainUrls = excludeContainUrls;
	}

	public String getStartRequestPaht() {
		return startRequestPaht;
	}

	public void setStartRequestPaht(String startRequestPaht) {
		this.startRequestPaht = startRequestPaht;
	}

	/**
	 * 模糊匹配字符串
	 * @param excludeContainUrls
	 * @param key
	 * @return
	 */
	private boolean vagueContain(List<String> excludeContainUrls,String key){
		for(String str : excludeContainUrls){
			if(key.contains(str)){
				return true;
			}
		}
		return false;
	}
}
